package jaux.tank.generator.component;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Insets;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import javax.swing.BorderFactory;
import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.WindowConstants;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;

import org.apache.commons.lang3.StringUtils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import jaux.tank.generator.domain.TankEnum;
import jaux.tank.generator.domain.TankEnum.FieldValueType;
import jaux.tank.generator.domain.TankEnum.TankEnumField;
import jaux.tank.generator.jd.components.JdTextField;
import jaux.tank.generator.jd.components.JdUniqueTextCellEditor;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class EnumEditorPanel extends JPanel{

	private static final String ENUM_NAME_COLUMN = "Enum Name";

	private static final long serialVersionUID = 1L;
	
	private TankEnum tankEnum;

	@Getter
	@Setter
	private boolean saved;


	private JPanel enumsPanel;

	private JPanel enumItemsPanel;

	private JTable enumItemsTable;

	private JTable fieldsTable;
	
	public EnumEditorPanel(TankEnum tankEnum) {
		super();
		this.tankEnum = tankEnum;
		initializeComponents();
	}
	
	private void initializeComponents() {
		setLayout(new BorderLayout(0, 0));
		
		// name and fields panel
		JPanel nameAndFieldsPanel = new JPanel();
		nameAndFieldsPanel.setLayout(new BorderLayout());
		add(nameAndFieldsPanel, BorderLayout.NORTH);
		// name panel
		JPanel namePanel = new JPanel();
		namePanel.setLayout(new FlowLayout(FlowLayout.LEFT));
		nameAndFieldsPanel.add(namePanel, BorderLayout.WEST);
		
		// name input
		JdTextField nameTextField = new JdTextField("Name:", tankEnum.getName());
		nameTextField.setEditable(false);
		nameTextField.onChange(value -> tankEnum.setName(value));
		namePanel.add(nameTextField );
		// fields toggle button
		JButton fieldsToggleButton = new JButton();
		fieldsToggleButton.setText(">>");
		fieldsToggleButton.setMargin(new Insets(0, 0, 0, 0));
		namePanel.add(fieldsToggleButton );
		
		// fields panel
		JPanel fieldsPanel = genFieldsPanel();
		
		nameAndFieldsPanel.add(fieldsPanel, BorderLayout.CENTER);
		fieldsPanel.setPreferredSize(new Dimension(0, 0));
		fieldsPanel.setVisible(false);
		fieldsToggleButton.addActionListener(e -> {
			if(fieldsPanel.isVisible()) {
				fieldsPanel.setVisible(false);
				fieldsPanel.setPreferredSize(new Dimension(0, 0));
				fieldsToggleButton.setText(">>");
			}else {
				fieldsPanel.setVisible(true);
				fieldsPanel.setPreferredSize(new Dimension(0, 200));
				fieldsToggleButton.setText("<<");
			}
		});
		
		// enums panel
		enumsPanel = new JPanel();
		enumsPanel.setLayout(new BorderLayout(0, 0));
		add(enumsPanel, BorderLayout.CENTER);
		enumItemsPanel = genEnumItemsPanel();
		enumsPanel.add(enumItemsPanel, BorderLayout.CENTER);
		
		
		 
	}


	private JPanel genFieldsPanel() {
		JPanel panel = new JPanel();
		panel.setBorder(BorderFactory.createTitledBorder("Fields"));
		panel.setLayout(new BorderLayout());
		Object[] columns= {"Name","Type"};
		// 创建表格数据
		List<Object[]> data = new ArrayList<>();
		if(tankEnum.getFields() != null) {
			for (TankEnumField field : tankEnum.getFields()) {
				data.add(new Object[] {field.getName(),field.getType()});
			}
			
		}
		// 创建默认的表格模型
		DefaultTableModel fieldsModel = new DefaultTableModel(data.toArray(new Object[0][0]), columns);
		fieldsTable = new JTable(fieldsModel );
		fieldsModel.addTableModelListener(e ->{
			List<TankEnumField> fields = new ArrayList<>();
			for(int i=0;i<fieldsTable.getRowCount();i++) {
				TankEnumField field = new TankEnumField();
				boolean aviliable = false;
				for(int j=0;j<fieldsTable.getColumnCount();j++) {
					Object value = fieldsTable.getValueAt(i, j);
					String column = fieldsTable.getColumnName(j);
					if(column.equals("Name") && value != null && StringUtils.isNotBlank((String)value)) {
						field.setName((String)value);
						aviliable = true;
					}else if(column.equals("Type")) {
						field.setType((TankEnum.FieldValueType)value);
					}
				}
				if(aviliable) {
					fields.add(field);
				}
			}
			tankEnum.setFields(fields);
			saved = false;
			
			refreshEnumItemsTable();
		});
		fieldsTable.setRowHeight(20); // 设置行高
		Supplier<Collection<String>> supplier = ()->tankEnum.getFields().stream().map(TankEnumField::getName).collect(Collectors.toSet());
		JdUniqueTextCellEditor fieldNameEditor = new JdUniqueTextCellEditor(supplier,"Field with the same name already exist!");
		fieldsTable.getColumnModel().getColumn(0).setCellEditor(fieldNameEditor);
		// use JComboBox for type column
		JComboBox<TankEnum.FieldValueType> fieldTypeComboBox = new JComboBox<>(TankEnum.FieldValueType.values());
		fieldsTable.getColumnModel().getColumn(1).setCellEditor(new DefaultCellEditor(fieldTypeComboBox));
		panel.add(new JScrollPane(fieldsTable), BorderLayout.CENTER);
		// add and del buttons
		JPanel buttonsPanel = new JPanel();
		buttonsPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
		panel.add(buttonsPanel, BorderLayout.SOUTH);
		// add button
		JButton addButton = new JButton();
		addButton.setText("add");
		addButton.addActionListener(e -> {
			((DefaultTableModel)fieldsTable.getModel()).addRow(new Object[] {"",TankEnum.FieldValueType.STRING});
			int rowCount = fieldsTable.getRowCount();
			fieldsTable.setRowSelectionInterval(rowCount-1, rowCount-1);
		});
		buttonsPanel.add(addButton);
		// del button
		JButton delButton = new JButton();
		delButton.setText("del");
		delButton.addActionListener(e -> {
			int selectedRow = fieldsTable.getSelectedRow();
			if(selectedRow == -1) {
				return;
			}
			((DefaultTableModel)fieldsTable.getModel()).removeRow(selectedRow);
			
			if(selectedRow > 0) {
				fieldsTable.setRowSelectionInterval(selectedRow-1, selectedRow-1);
			}
			
		});
		buttonsPanel.add(delButton);
		
		return panel;
	}


	private void refreshEnumItemsTable() {
		stopEnumItemsTableEditing();
		Map<String, Object[]> enumItems = new HashMap<>();
		if(enumItemsTable != null) {
			int rows = enumItemsTable.getRowCount();
			int cols = enumItemsTable.getColumnModel().getColumnCount();
			for(int i=0;i<rows;i++) {
				String enumItemName = ( String)enumItemsTable.getValueAt(i, 0);
				if(StringUtils.isBlank(enumItemName)) {
					continue;
				}
				List<Object> enumItemData = new ArrayList<>(); 
				for(int j=1;j<cols;j++) {
					String colName = enumItemsTable.getColumnName(j);
					if(tankEnum.getFields()==null ||  tankEnum.getFields().stream().noneMatch(field->field.getName().equals(colName))) {
						continue;
					}
					enumItemData.add(enumItemsTable.getValueAt(i, j) );
				}
				enumItems.put(enumItemName, enumItemData.toArray());
			}
		}
		log.debug("set new enum items: " + enumItems);
		tankEnum.setEnums(enumItems);
		enumsPanel.remove(enumItemsPanel);
		enumItemsPanel = genEnumItemsPanel();
		enumsPanel.add(enumItemsPanel, BorderLayout.CENTER);
		enumsPanel.updateUI();
	}

	private JPanel genEnumItemsPanel() {
		JPanel panel = new JPanel();
		panel.setBorder(BorderFactory.createTitledBorder("Values"));
		panel.setLayout(new BorderLayout(0, 0));
		// enum values table
		List<String> columnLst =  new ArrayList<>(Arrays.asList(ENUM_NAME_COLUMN));
		Map<Integer, TableCellEditor> columnEditors = new HashMap<>();
		
		// check enum item name exists
		DefaultCellEditor enumItemNameEditor = new JdUniqueTextCellEditor(()->tankEnum.getEnums().keySet(), "Enum with the same name already exist! ");
		columnEditors.put(0, enumItemNameEditor);
		if(tankEnum.getFields()!=null) {
			int index = 1;
			for (TankEnumField field : tankEnum.getFields()) {
				columnLst.add(field.getName());
				if(field.getType() ==FieldValueType.BOOLEAN) {
					JComboBox<Boolean> comboBox = new JComboBox<>(new Boolean[] {true,false} );
					columnEditors.put(index, new DefaultCellEditor(comboBox));
				}
				index++;
			}
		}
		Object[] columns= columnLst.toArray();
		// 创建表格数据
		List<Object[]> data = new ArrayList<>();
		if(tankEnum.getEnums() != null) {
			for ( Map.Entry<String, Object[]> enumItem : tankEnum.getEnums().entrySet()) {
				List<Object> values = new ArrayList<>();
				values.add(enumItem.getKey());
				if(enumItem.getValue() != null) {
					values.addAll(Arrays.asList(enumItem.getValue()));
				}
				data.add(values.toArray(new Object[0]));
			}
			
		}
		// 创建默认的表格模型
		DefaultTableModel enumItemsModel = new DefaultTableModel(data.toArray(new Object[0][0]), columns);
		enumItemsTable = new JTable(enumItemsModel );
		enumItemsTable.setRowHeight(20); // 设置行高
		enumItemsTable.addFocusListener(new FocusAdapter() {
			@Override
			public void focusGained(FocusEvent e) {
				super.focusGained(e);
				stopFieldsTableEditing();
			}
		});
		columnEditors.forEach((k,v) -> enumItemsTable.getColumnModel().getColumn(k).setCellEditor(v));

		panel.add(new JScrollPane(enumItemsTable), BorderLayout.CENTER);
		enumItemsModel.addTableModelListener(e ->{
			Map<String, Object[]> enums = new LinkedHashMap<>();
			for(int i=0;i<enumItemsTable.getRowCount();i++) {
				boolean avilable = false;
				String enumName = null;
				List<Object> values = new ArrayList<>();
				for(int j=0;j<enumItemsTable.getColumnCount();j++) {
					Object value = enumItemsTable.getValueAt(i, j);
					String column = enumItemsTable.getColumnName(j);
					if(column.equals(ENUM_NAME_COLUMN) && value != null && StringUtils.isNotBlank((String)value)) {
						enumName = (String)value;
						avilable = true;
					}else  {
						values.add(value);
					}
				}
				if(avilable && enumName != null) {
					enums.put(enumName,values.toArray());
				}
			}
			tankEnum.setEnums(enums);
			saved = false;
		});
		// add and del buttons
		
		
		// enum operations
		JPanel enumOperationsPanel = new JPanel();
		enumOperationsPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
		panel.add(enumOperationsPanel, BorderLayout.SOUTH);
		// enum add
		JButton enumAddButton = new JButton();
		enumAddButton.addActionListener(e -> {
			DefaultTableModel tableModel = (DefaultTableModel)enumItemsTable.getModel();
			tableModel.addRow(new Object[tableModel.getColumnCount()] );
			int rowCount = enumItemsTable.getRowCount();
			enumItemsTable.setRowSelectionInterval(rowCount-1, rowCount-1);
		});
		enumAddButton.setText("add");
		enumOperationsPanel.add(enumAddButton);
		// enum delete
		JButton enumDeleteButton = new JButton();
		enumDeleteButton.setText("delete");
		enumDeleteButton.addActionListener(e -> {
			int selectedRow = enumItemsTable.getSelectedRow();
			if(selectedRow == -1) {
				return;
			}
			((DefaultTableModel)enumItemsTable.getModel()).removeRow(selectedRow);
			if(selectedRow > 0) {
				enumItemsTable.setRowSelectionInterval(selectedRow-1, selectedRow-1);
			}
		});
		enumOperationsPanel.add(enumDeleteButton);
		return panel;
	}
	
	public TankEnum getTankEnum() {
		stopFieldsTableEditing();
		stopEnumItemsTableEditing();
		return tankEnum;
	}

	private void stopEnumItemsTableEditing() {
		TableCellEditor activateEditor;
		activateEditor = enumItemsTable.getCellEditor();
		if(activateEditor !=null) {
			activateEditor.stopCellEditing();
		}
	}

	private void stopFieldsTableEditing() {
		TableCellEditor activateEditor = fieldsTable.getCellEditor();
		if(activateEditor !=null) {
			activateEditor.stopCellEditing();
		}
	}
	
	public static void main(String[] args) {
		// test
		JFrame frame = new JFrame();
		frame.setLayout(new BorderLayout(0, 0));
		frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
		TankEnum tankEnum = new TankEnum("test");
		EnumEditorPanel enumEditorPanel = new EnumEditorPanel(tankEnum);
		frame.getContentPane().add(enumEditorPanel, BorderLayout.CENTER);
		// get value button
		JButton getValueButton = new JButton();
		getValueButton.setText("get value");
		getValueButton.addActionListener(e -> {
			String enumJSON = null;
			try {
				enumJSON = new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(enumEditorPanel.getTankEnum());
			} catch (JsonProcessingException e1) {
				e1.printStackTrace();
			}
			JOptionPane.showMessageDialog(frame, enumJSON);
		});
		frame.getContentPane().add(getValueButton, BorderLayout.SOUTH);
		frame.pack();
		frame.setVisible(true);
	}
	
}
